> If you need to list things ordered by creation time very often, it’s useful if the primary index gives that behavior, removing the need for another index.
I thought the main reason UUIDv7 was created with a time component is to keep temporally related rows together in the index pages. Randomly distributed row ids seem OK until the entire index no longer fits in RAM.
It's not just index pages. Generally databases write records into pages, which are then grouped into extents. If the primary key gives an ordering you can create new pages and start filling them up in an orderly fashion. If it's random, you are doing random inserts into pages everywhere all the time, then you start running out of room in pages and have to start splitting them. UUIDv7 solves the most painful parts of this problem in general and is a much better option for a Primary Key than UUIDv1-4.
I suppose it's a problem if the spec actually says they're monotonically incrementing because it's clear that isn't guaranteed. But that was never a property I wanted or expected out of them anyway.
All of the other issues here sound like misunderstandings or misuses of ids, IMHO
Yeah, this is 99% of the benefit for me. And it's not theoretical. My company saw performance issues using pure random IDs with MySQL way back in 2015; and consequently rolled our own UUIDv7 that we're still using to this day.
The biggest advantage I've seen for UUID come with distributed systems imo. The ability to create an id without lock coordination allows for much easier horizontal scaling. UUIDv1 avoids the collision issue by using a portion of the MAC in the id, but v1-4 all have sorting issues. UUIDv7 is a good choice if your use case warrants it. If you can get away with a single integer field like a database primary key and not get bottlenecked that's much easier. When that doesn't work anymore one of the distributed id schemes like UUID are the only real options available.
Yes, UUIDv7 doesn't have the same charm and texture as tree notches. But it's been a huge help with my tables containing billions of records.
Sure, we could use faster alternatives specifically for indexes - but UUIDs are used throughout all business processes. Setting up a separate "conversion to UUID" step seems like a waste.
I support this. I wrote a Sqlite plugin for KSUIDs, but at the end I never used them, since all I do can be handled by integer primary keys, which sqlite maps to the rowid automatically. Autoincrement is only required, if you want to prevent the reuse of ids in deleted rows.
That depends on your database. Serial keys can create hot spots in distributed databases because new writes are consistently directed to the same partition.
There's no fundamental reason for this. Much as with per-CPU memory allocation, shards could pre-reserve chunks of the next serial space, and then only coordinate when they need a new chunk.
Yeah this was odd: "Superficially, it seems that embedding a timestamp in the ID can remove the need to track the creation time in a separate database column" Who actually tried to do this?
I didn't assume uuid7 was guaranteed monotonic either. Of course not if you have different clocks. That's not the point of it.
Kind of funny title since I didn’t think UUIDv7 had any of the properties being disputed here. The reason to use UUIDv7 is because it reduces the space over which the UUID can cover at a given time, which improves write clustering. They don’t need to be strictly ordered for this, just “good enough”.
I hope to god no one is actually using the timestamp portion to replace an “updated at” timestamp but I suspect I’ll be disappointed.
This is perfect if you only have one machine and the process never restarts. But then if you only have one machine you don’t realistically need uuid identifiers.
This is why the oldschool UUIDv1 spec specifies initializing the sequence value at a random number:
“12.4.4 — The Clock Sequence shall be originally (that is, once in the lifetime of a system producing UUIDs) initialized to a random number that is not derived from the Node value. NOTE – This is in order to minimize the correlation across systems, providing maximum protection”
I feel like this article way downplays the entire point of UUIDv7s, which is that it lets you sort (and store!) your data kinda sorta by creation time, no overlap ever. That's great for cursors, for streaming data, for twitter clones, for chat messages, for player moves, and so on and so forth. Nearly everything that's vaguely realtimey that you might like to both persist and broadcast live to other clients, and displayed in a stable & reasonable order, benefits from a property like this.
In fairness, he does mention this, but squeezes it into a single sentence near the end. But eg the fact that you can't reliably extract the precise timestamp from the UUIDv7 is a feature, not a bug. It's either that or collisions and you don't want the collisions.
Yeah, I think the key here is that you have to know what the limitations are. UUIDv7 does _not_ provide you with sorted and ordered IDs by creation time, it provides you with _kinda sorta_ sorted and ordered IDs by creation time. That's probably fine for a lot of use cases, but if it's not OK for yours (forensic accounting, risk mitigation for banks, whatever), you can't use it for that. You're right it's not a technical problem necessarily, but when the marketing material describes something without an asterisk, people who are unaware that what's being described is impossible at the limits can make bad system design decisions based on that promise.
>Unfortunately, the timestamp embedded in a UUIDv7 ID may not relate to the object creation time. The Postgres docs for uuid_extract_timestamp helpfully warn that “the extracted timestamp is not necessarily exactly equal to the time the UUID was generated”.
I've never bothered with anything but uuid4. Yes there are special circumstances where a public uuid7 helps because you're sure the timestamp leakage is ok, and you made sure it's not guessable, and you're sure some index performs better with it in a way that matters. There are also certain distributed systems where it has helpful properties. The amount of times blanket advice is given to use uuid7 far outweighs how often I've been in that situation, especially when people are talking about Postgres primary keys.
I assume I'm oblivious and naive, but it seems theoretically possible these issues could -- theoretically -- result in TOCTOU or similar issues.
Any thoughts about relying on:
- central time service, with message-queue of inbound requests, resulting in correctly timed + sequenced events [ albeit with hopefully small lag tradeoffs ]?
- know of any databases that implement this type of logic around their own minting of time-stamped UUIDs?
Prior efforts implementing event-driven backend service with UUID7s minted in client-code seemed perfectly fine at low usage/scale. However, as clients + usage + event-volume scaled, team became more curious about time/timestamp/related issues, and any minting client-side quickly fell out of favor.
Wow... That is some nitpicking... The major issue these UUIDs solve for me is the fact that using a random UUID as a primary key of a relational database leads to horrible performance...
It has never crossed my mind to decode the timestamp from them so I save myself a 12-byte column, neither has it crossed my mind to have them monotonic in a distributed system...
The small saving grace for me is that, despite my broken mental model of UUIDv7's time and monotonicity properties, I get to also use SQLite's automatic rowid for ordering and disambiguation. That too, only because I can (still think I can) get away with a "DB per tenant" architecture. Nevertheless, the same breakage potential will return when (not if, when) I have to mix / remix records from two or more DB files.
brb, investigating whether I can reliably construct UUIDv7s using db-time...
> Superficially, it seems that embedding a timestamp in the ID can remove the need to track the creation time in a separate database column, providing marginally more efficient database operations. If you need to list things ordered by creation time very often, it’s useful if the primary index gives that behavior, removing the need for another index.
This glares for me - I'm not "listing" by creation time per se; I'm taking advantage of locality - the page of data that a given key's data is in should also contain data of other keys created around the same time.
That locality means that I have less disk calls if I want to get the data for a series of rows that were created around the same time.
The blog post seems to be saying that (worst case) I cannot rely on that aspect.
It doesn’t have anything to do with indexing more than one thing.
The issue with truly random IDs is that inserts are scattered throughout the B-tree index, which cause page splits, fragmentation, and poor cache locality. Using a timestamp prefix makes new IDs sequential, so inserts tend to land near the end of the index, which avoid all the B-tree trashing and improve write performance.
Yes, which means that you should probably be able to provide your timestamp to the database so it can use it for optimizing its index, rather than trying to shove it in your ID
Sure but at least you are being honest about it. For what it's worth the database can handle this internally without you needing to hand this back but if you have a bad one then yes you should probably have it give you back a location hint that you use for future queries
> My mental model for UUIDv7 is that it’s a mash-up of a bunch of different ideas which implementations are free to choose as they see fit. There’s a wide variance of properties that UUIDv7 implementations can provide, with varying degrees of certainty they can successfully provide those properties.
I think this is good. A proper UUID that has specific significant bits dedicated to (approximately) monotonic values is useful. I don't want it over-specified beyond that, and I don't want to just use UUIDv8.
I thought the main reason UUIDv7 was created with a time component is to keep temporally related rows together in the index pages. Randomly distributed row ids seem OK until the entire index no longer fits in RAM.
I suppose it's a problem if the spec actually says they're monotonically incrementing because it's clear that isn't guaranteed. But that was never a property I wanted or expected out of them anyway.
All of the other issues here sound like misunderstandings or misuses of ids, IMHO
I know the conventional wisdom says that the collisions are extremely rare <insert some sort of cosmis analogy>
Then, some reddit users reported encountering a collision: https://www.reddit.com/r/webdev/comments/1kvkxte/comment/mud...
Ultimately, you'd have to design the system to handle collision anyway. Might as well design a system that avoid collision in the first place.
Sure, we could use faster alternatives specifically for indexes - but UUIDs are used throughout all business processes. Setting up a separate "conversion to UUID" step seems like a waste.
Some of us don't, so these types of universal proclamations are rather unhelpful.
for AI heavy apps/agent they are token expensive
I didn't assume uuid7 was guaranteed monotonic either. Of course not if you have different clocks. That's not the point of it.
the UUID timestamp is not for de-duping purposes. it's avoid expensive re-indexes that happen with truly random ids
I hope to god no one is actually using the timestamp portion to replace an “updated at” timestamp but I suspect I’ll be disappointed.
I believe it was Poincare who has proven that it's even theoretically impossible to synchronize clocks exactly.
But if you did want "monotonically increasing IDs", as the article suggests, that is what you want.
“12.4.4 — The Clock Sequence shall be originally (that is, once in the lifetime of a system producing UUIDs) initialized to a random number that is not derived from the Node value. NOTE – This is in order to minimize the correlation across systems, providing maximum protection”
In fairness, he does mention this, but squeezes it into a single sentence near the end. But eg the fact that you can't reliably extract the precise timestamp from the UUIDv7 is a feature, not a bug. It's either that or collisions and you don't want the collisions.
Just like NOW() won't have the current time.
I assume I'm oblivious and naive, but it seems theoretically possible these issues could -- theoretically -- result in TOCTOU or similar issues.
Any thoughts about relying on:
- central time service, with message-queue of inbound requests, resulting in correctly timed + sequenced events [ albeit with hopefully small lag tradeoffs ]?
- know of any databases that implement this type of logic around their own minting of time-stamped UUIDs?
Prior efforts implementing event-driven backend service with UUID7s minted in client-code seemed perfectly fine at low usage/scale. However, as clients + usage + event-volume scaled, team became more curious about time/timestamp/related issues, and any minting client-side quickly fell out of favor.
Any insights greatly appreciated -- thanks
[ edited b/c i don't know how to format... ]
There are fun conditions under which a quasicrystalline clock could coordinate this in a distributed way. https://en.wikipedia.org/wiki/Time_crystal
It has never crossed my mind to decode the timestamp from them so I save myself a 12-byte column, neither has it crossed my mind to have them monotonic in a distributed system...
Realities are arrows. Time marks flight. UUIDv7 is Time.
(Yes. Yes. No, hell no, not after what Robert just told ya.)
https://www.evalapply.org/posts/poor-mans-time-oriented-data....
The small saving grace for me is that, despite my broken mental model of UUIDv7's time and monotonicity properties, I get to also use SQLite's automatic rowid for ordering and disambiguation. That too, only because I can (still think I can) get away with a "DB per tenant" architecture. Nevertheless, the same breakage potential will return when (not if, when) I have to mix / remix records from two or more DB files.
brb, investigating whether I can reliably construct UUIDv7s using db-time...
This glares for me - I'm not "listing" by creation time per se; I'm taking advantage of locality - the page of data that a given key's data is in should also contain data of other keys created around the same time.
That locality means that I have less disk calls if I want to get the data for a series of rows that were created around the same time.
The blog post seems to be saying that (worst case) I cannot rely on that aspect.
The issue with truly random IDs is that inserts are scattered throughout the B-tree index, which cause page splits, fragmentation, and poor cache locality. Using a timestamp prefix makes new IDs sequential, so inserts tend to land near the end of the index, which avoid all the B-tree trashing and improve write performance.
I think this is good. A proper UUID that has specific significant bits dedicated to (approximately) monotonic values is useful. I don't want it over-specified beyond that, and I don't want to just use UUIDv8.