ricardobeat

This is a misunderstanding of the purpose of UUIDv7. The whole point is to keep rows physically close, so you can very quickly extract an entire time range from a number of database shards, with minimal reads and compute. And be able to generate them without a centralized server. That's all there was to it, and something you'd only worry about when serving billions of records every hour.

They are also not supposed to be used as public IDs, just like you wouldn't use auto-incrementing IDs that way.

(I wrote a library for this 13y ago [1], around the time Twitter, Instagram et al started reaching enough scale to think about this kind of optimization)

[1] https://github.com/ricardobeat/unik

show comments
hackingonempty

> 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.

show comments
btown

One of my favorite things about UUIDv7: the package https://pypi.org/project/uuid7/ does not implement compliant UUIDv7s, it's from a wholly incompatible draft format. That was not a fun debugging session.

https://pypi.org/project/uuid-utils/ is what you want :)

https://github.com/stevesimmons/uuid7/issues/1

show comments
kentm

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.

show comments
tossandthrow

I have had this conversation with the team. I lead, who started to use properties of the ID for ordering etc.

An ID is an identifiers. It supports exactly the operation of comparing two IDs to establish identity.

When you start to treat them as something else that is not guaranteed, you will see weird things happening.

show comments
sedatk

You probably don't need a UUID: https://ssg.dev/you-probably-dont-need-a-uuid/

show comments
rcoder

If you really need monotonic, secure/unguessable IDs, there are options like CUID2 (github.com/paralleldrive/cuid2).

Older designs like ULIDs (https://github.com/ulid/spec) and Snowflake IDs (https://en.wikipedia.org/wiki/Snowflake_ID) have similar properties to UUIDv7, and potentially some advantages w.r.t. length/encoding/runtime cost/etc.

But UUIDv7 has one key advantage none of those do: you can store them in a UUID-typed column or struct field without any lossy conversion. All of the UUID versions share a common size and serialization format, so it doesn't actually matter if you generate v1/4/7/etc.

Many language standard libraries can generate them interchangeably, and (for example) a Postgres table can store any of the versions in the same column type.

So yes, to the author's point(s): they are not magic, monotonic, un-guessable IDs and you shouldn't treat them as either "secure" or strictly ordered. That doesn't mean they aren't useful.

show comments
0x20cowboy

As others have said, I don’t think the time was meant to be something you relied on for the object time, but more the help with indexing and sharding.

show comments
tanin

I generally avoid using UUID unless there's a strong reason for it.

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, we'd have to design the system to handle collision anyway. Might as well design a system to avoid collision in the first place. Then, if we have a system that avoids collision, then whether UUID is used is irrelevant.

show comments
computing

perfect example of missing the forest for the trees

the UUID timestamp is not for de-duping purposes. it's avoid expensive re-indexes that happen with truly random ids

dspillett

> 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.

If you are using your primary key for anything other than a unique identifier for rows, you are almost certainly doing something wrong. If you need an index on creation time, or just creation order, have a creation time column and an index on that.

show comments
real_faxenoff

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.

skrebbel

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.

show comments
mahboi

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.

nitwit005

The Python version simply forces the timestamp forward if needed to preserve ordering:

  if timestamp_ms < _last_timestamp_v7:
    timestamp_ms = _last_timestamp_v7 + 1
https://github.com/python/cpython/blob/8d119208074fef0f652f1...

But if you did want "monotonically increasing IDs", as the article suggests, that is what you want.

show comments
jefecoon

Great article.

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... ]

show comments
taylorbuley

> the problem is that system clocks cannot be synchronized to a suitable precision.

There are fun conditions under which a quasicrystalline clock could coordinate this in a distributed way. https://en.wikipedia.org/wiki/Time_crystal

gcbirzan

>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”.

Just like NOW() won't have the current time.

pupppet

I dislike UUIDv7 for no other reason than I hate any UUID with a dash. Makes navigating/selecting ids a pain with Ctrl/Cmd left/right arrow.

show comments
felipelalli

What an exaggeration!

deepsun

> not necessarily exactly equal to the time

I believe it was Poincare who has proven that it's even theoretically impossible to synchronize clocks exactly.

show comments
phoronixrly

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...

show comments
simonbuilds

So where do you actually draw the line for a business and a customers table whether to use uuidv4 vs uuidv7, in terms of privacy and efficiency?

show comments
adityaathalye

alexsci destroyed one of my core assumptions in a bitemporal data system project I've been noodling on. Namely, this oh-so-confident presumption:

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...

awesome_dude

> 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.

show comments
saagarjha

Just use UUIDs as they were meant to be used, as a random identifier. Don't shove random other junk into it!

show comments
pphysch

> 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.

jongjong

UUIDv7 is not that useful. I'm still using mostly UUIDv4 and UUIDv5.

I'm leaning increasingly heavily on v5 for its determinism which is extremely important for IDs. I would sacrifice every other property to get determinism.

You can't have idempotency without determinism and you can't get reliability without idempotency.

Once you factor in failure and retry scenarios under concurrency, you often need ID determinism. In the rare cases I don't want determinism, I prefer to go all-in on randomness. I prefer my IDs to be well spread out; it scales better, more predictable, less of a security liability (prefer not to have all data clustered in the same place)...

Both UUIDv4 and UUIDv5 outputs are uniformly distributed. IMO this is a desirable property which is well worth a bit of additional compute. v7 feels like an optimization hack, and it comes at a cost.

If you want to get the performance benefits of v7 with the determinism of v5, then you need to have essentially two IDs; the id (v7) and an idempotency key (which is essentially like a second ID you have to maintain; e.g. UUIDv5)... In the end, maintaining a UUIDv7 ID + idempotency key is less performant than just having a single UUIDv5 id which does both jobs for no extra effort... Not to mention, less maintainable and more complicated to use.

show comments
ForHackernews

Most (all?) of these are also true of UUIDv1. Devs in 2026 should be well versed in using UUIDv4 if they really truly can never leak anything.