by craigkerstiens on 1/2/25, 4:32 PM with 104 comments
by kingkilr on 1/2/25, 5:05 PM
The reason for this is simple: the documentation doesn't promise this property. Moreover, even if it did, the RFC for UUIDv7 doesn't promise this property. If you decide to depend on it, you're setting yourself up for a bad time when PostgreSQL decides to change their implementation strategy, or you move to a different database.
Further, the stated motivations for this, to slightly simplify testing code, are massively under-motivating. Saving a single line of code can hardly be said to be worth it, but even if it were, this is a problem far better solved by simply writing a function that will both generate the objects and sort them.
As a profession, I strongly feel we need to do a better job orienting ourselves to the reality that our code has a tendency to live for a long time, and we need to optimize not for "how quickly can I type it", but "what will this code cost over its lifetime".
by mmerickel on 1/2/25, 6:02 PM
by fngjdflmdflg on 1/2/25, 5:09 PM
>It makes a repeated UUID between processes more likely, but there’s still 62 bits of randomness left to make use of, so collisions remain vastly unlikely.
Does it? Even though the number of random bits has decreased, the time interval to create such a duplicate has also decreased, namely to an interval of one nanosecond.
by samatman on 1/2/25, 7:34 PM
Let's say you need an opaque unique handle, and a timestamp, and a monotonically increasing row ID. Common enough. Do they have to be the same thing? Should they be the same thing? Because to me that sounds like three things: an autoincrementing primary key, a UUIDv4, and a nanosecond timestamp.
Is it always ok that the 'opaque' unique ID isn't opaque at all, that it's carrying around a timestamp? Will that allow correlating things which maybe you didn't want hostiles to correlate? Are you 100% sure that you'll never want, or need, to re-timestamp data without changing its global ID?
Maybe you do need these things unnormalized and conflated. Do you though? At least ask the question.
by Dylan16807 on 1/2/25, 10:57 PM
A millisecond divided by 4096 is not a nanosecond. It's about 250 nanoseconds.
by scrollaway on 1/2/25, 7:50 PM
I want to share a django library I wrote a little while back which allows for prefixed identity fields, in the same style as Stripe's ID fields (obj_XXXXXXXXX):
https://github.com/jleclanche/django-prefixed-identity-field...
This gives a PrefixedIdentityField(prefix="obj_"), which is backed by uuid7 and base58. In the database, the IDs are stored as UUIDs, which makes them an efficient field -- they are transformed into prefixed IDs when coming out of the database, which makes them perfect for APIs.
(I know, no documentation .. if someone wants to use this, feel free to file issues to ask questions, I'd love to help)
by dotdi on 1/2/25, 5:13 PM
ULID does specifically require generated IDs to be monotonically increasing as opposed to what the RFC for UUIDv7 states, which is a big deal IMHO.
by lordofgibbons on 1/2/25, 5:20 PM
We've been using an implementation of it in Go for many years in production without issues.
by willvarfar on 1/2/25, 7:08 PM
I have used this guarantee for events generated on clients. It really simplifies a lot of reasoning.
by wslh on 1/2/25, 11:13 PM
by Glyptodon on 1/2/25, 7:31 PM
by kagitac on 1/3/25, 5:10 PM
If anyone is interested, here is the package: https://github.com/cmackenzie1/go-uuid. It also includes a CLI similar to that of `uuidgen`, but supports version 7.
by dfee on 1/2/25, 10:59 PM
Collection<UUID> generate(final int count);
I also have an interface that I can back with a RNG that generates auto incrementing values, sorts for testing, I have the experience of ints, but for production, my non-timestamp component is random.by pphysch on 1/2/25, 5:49 PM
"extra_" or "distinct_" would be a more accurate prefix for UUIDv7.
UUIDv7 is actually quite a flexible standard due to these two underspecified fields. I'm glad Postgres took advantage of that!
by nikisweeting on 1/2/25, 9:30 PM
My implementation supports graceful degradation between nanosecond scale resolution, microsecond, and millisecond, by using 12 bits for each and filling up the leftmost bits of rand_a and rand_b. Not all environments provide high resolution system clocks with no drift, so it's is important to maintain monotonicity when generating IDs with a low-res timestamp as input. You still want the bits that would've held the nanosecond value to be monotonic.
Neither of the existing uuid_utils and uuid7 python libs that can generate UUID7s support this monotonicity property.
Am planning on using this for ArchiveBox append-only "snapshot" records, which are intrinsically linked to time, so it's a good use-case imo.
There's another great resource here that I think is one of the best explainers of UUIDv7: https://antonz.org/uuidv7/
Whatever you do, don't implement the cursed 36-bit whole-second based time UUIDv7 variant that you occasionally see on StackOverflow / blog posts, stick to 48!
by hardwaresofton on 1/3/25, 6:10 AM
by urronglol on 1/2/25, 4:49 PM